package com.bluedot.dao.pool;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.Properties;

/**
 * 连接池类
 * 管理数据库连接
 *
 * 实现业务逻辑：
 *
 * -若有用户请求连接，则将空闲连接链表的头结点从链表中移出，让用户使用
 * -若有用户关闭连接，则重置该连接开始空闲时间，并将其加入空闲连接链表队尾
 *
 * -没有空闲连接时，主动创建一定数目空闲连接，但要保证总连接数不超过最大连接数
 * -当总连接数小于最小连接数时，主动创建一定数目空闲连接，保证总连接数不超过最大连接数
 *
 * -当总连接数大于最大连接数时，关闭空闲连接
 * -当连接的目前空闲时间大于最大空闲时间时，关闭该连接
 *
 * -当连接池已满，仍有用户请求连接时，先让其等待最大空闲时间，如果之后连接池仍然无空闲连接时，报告获取连接失败，请稍后重试错误
 */
public class BluedotConnectionPool {
    /**
     *属性1：驱动路径，从配置文件获取
     */
    private String driver;
    /**
     *属性2：DBC连接URL，从配置文件获取
     */
    private String url;
    /**
     *属性3：数据库账号，从配置文件获取
     */
    private String username;
    /**
     *属性4：数据库密码，从配置文件获取
     */
    private String password;
    /**
     *属性5：初始数据库连接数，从配置文件获取
     */
    private int initPoolSize;
    /**
     *属性6：最小数据库连接数，从配置文件获取
     */
    private int minPoolSize;
    /**
     *属性7：最大数据库连接数，从配置文件获取
     */
    private int maxPoolSize;
    /**
     *属性8：数据库连接最大空闲时间，从配置文件获取
     */
    private int maxIdleTime;
    /**
     *属性9：目前的数据库连接数（包括空闲数据库连接和已分配数据库连接）
     */
    private int nowPoolSize=0;
    /**
     *属性10：数据库空闲连接的集合,LinkedList集合:add()尾插返回Boolean,pop()头删返回Connection
     */
    private LinkedList <BluedotConnection> pool= new LinkedList <>();
    /**
     *属性11：默认自动增长连接数，从配置文件获取
     */
    private int defaultGrow;

    /**
     * 构造方法，为20个属性设置初始值
     */
    public BluedotConnectionPool(){
        try {
            /**
             * 加载配置文件
             */
            Properties properties = new Properties();
            InputStream resourceAsStream = BluedotConnectionPool.class.getClassLoader().getResourceAsStream("wsConnectionPool.properties");
            properties.load(resourceAsStream);
            /**
             * 从配置文件中读取相关属性值
             */
            driver=properties.getProperty("driver");
            url=properties.getProperty("url");
            username=properties.getProperty("username");
            password=properties.getProperty("password");
            initPoolSize=Integer.parseInt(properties.getProperty("initPoolSize"));
            minPoolSize=Integer.parseInt(properties.getProperty("minPoolSize"));
            maxPoolSize=Integer.parseInt(properties.getProperty("maxPoolSize"));
            maxIdleTime=Integer.parseInt(properties.getProperty("maxIdleTime"));
            defaultGrow= Integer.parseInt(properties.getProperty("defaultGrow"));
            /**
             * 数据库连接池初始化配置检查
             *
            /*
             * 1.检查最大空闲时间是否大于零
             */
            if(maxIdleTime<=0){
                throw new Exception();
            }
            /**
             * 2.检查最小连接数是否小于等于最大连接数
             */
            if(minPoolSize>maxPoolSize){
                throw new Exception();
            }
            /**
             * 3.检查初始连接数是否小于等于最大连接数，是否大于等于最小连接数
             */
            if(initPoolSize<minPoolSize||initPoolSize>maxPoolSize){
                throw new Exception();
            }
            /**
             * 根据从配置文件中得到的属性值设置其他属性:
             * 创建初始连接数个数据库连接，并更新目前的数据库连接数
             */
            growConnection(initPoolSize);
            System.out.println("数据库连接池创建项目！！");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("配置信息错误,连接池初始化失败!");
            System.exit(0);
        }
    }

    /**
     * 已经分配连接数
     * @return
     */
    public Integer getBusyConnectionNum() {
        return nowPoolSize - pool.size();
    }


    /**
     * 当前空闲连接数
     * @return
     */
    public Integer getIdleConnectionNum() {
        return pool.size();
    }

    /**
     * 创建一定数目的空闲连接的方法
     */
    /**
     * 创建一个空闲连接
     */
    private void growConnection(){
        BluedotConnection wsConnection = new BluedotConnection(driver, url, username, password);
        pool.add(wsConnection);
        addNowConnection();
    }

    /**
     * 创建指定数目的空闲连接
     */
    private void growConnection(int time){
        for (int i = 0; i < time; i++) {
            growConnection();
        }
    }

    /**
     * 连接池关闭连接的方法
     */
    /**
     * 关闭指定的数据库连接
     */
    private void closeConnection(BluedotConnection wsConnection){
        wsConnection.closeConnection();
        deleteNowConnection();
    }

    /**
     * 检查空闲连接链表，查找目前空闲时间大于最大空闲时间的连接并关闭
     */
    private void closeIdleConnection(){
        BluedotConnection wsConnection=null;
        for (int i = 0; i < pool.size(); i++) {
            wsConnection=pool.get(i);
            if(wsConnection.getNowIdleTime()>maxIdleTime){
                pool.remove(i);
                i--;
                closeConnection(wsConnection);
            }
        }
    }

    /**
     * 关闭已分配给用户的连接（实际上是将该连接尾插入空闲连接链表，并重置该连接的开始空闲时间）
     */
    private void closeUsingConnection(BluedotConnection wsConnection){
        pool.add(wsConnection);
        wsConnection.setStartIdleTime();
    }

    /**
     * 处理目前连接数和空闲连接数的方法
     */
    /**
     * 增加目前连接数,如果目前连接数大于最大连接数，释放连接
     */
    private void addNowConnection(){
        nowPoolSize++;
        while(nowPoolSize>maxPoolSize){
            closeIdleConnection();
        }
    }
    /**
     * 减少目前连接数,如果目前连接数小于最小连接数，建立自动增长连接数个空闲连接
     */
    private void deleteNowConnection(){
        nowPoolSize--;
        if(nowPoolSize<minPoolSize){
            growConnection(defaultGrow);
        }
    }
    /**
     * 检测空闲连接数,如果空闲连接为零，创建自动增长连接数个空闲连接
     */
    private void isHaveIdleConnection(){
        if(pool.size()==0){
            growConnection(defaultGrow);
        }
    }

    /**
     * 用户请求连接的方法
     */
    public Connection getConnection(){
        Connection connection=null;
        if(nowPoolSize==maxPoolSize&&pool.size()==0){
            try {
                Thread.sleep(maxIdleTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(pool.size()==0){
            System.out.println("获取连接失败，请稍后重试!");
            System.exit(0);
        }
        connection=pool.pop().getConnection();
        return connection;
    }
    /**
     * 用户释放连接的方法
     */
    public void releaseConnection(Connection connection){
        closeUsingConnection(new BluedotConnection(connection));
    }

}

